package com.vhly.ddxdecompiler.dexreader.file.mixeditem;

import com.vhly.classfile.*;
import com.vhly.classfile.bytecode.BCD;
import com.vhly.ddxdecompiler.dexreader.file.DexFile;
import com.vhly.ddxdecompiler.dexreader.file.constant.*;
import com.vhly.ddxdecompiler.dexreader.file.dalv.code.DalvInsn;
import com.vhly.ddxdecompiler.dexreader.file.dalv.code.DalvInsnList;
import com.vhly.ddxdecompiler.dexreader.file.dalv.code.InsnConvert;
import com.vhly.ddxdecompiler.dexreader.io.DexInputStream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * Created by IntelliJ IDEA.
 * User: vhly
 * Date: 2009-10-15
 * Time: 12:40:34
 *
 * @author vhly[FR]
 */
public final class ClassDataItem extends OffsettedItem {

    private DexFile dex;

    ArrayList<EncodeField> staticFields;
    private HashMap staticValues;
    private ArrayList<EncodeField> instanceFields;
    private ArrayList<EncodeMethod> directMethods;
    private ArrayList<EncodeMethod> virtualMethods;

    public ClassDataItem(DexFile dexFile) {
        super(4, 0);
        dex = dexFile;
        staticFields = new ArrayList<EncodeField>();
        instanceFields = new ArrayList<EncodeField>();
        directMethods = new ArrayList<EncodeMethod>();
        virtualMethods = new ArrayList<EncodeMethod>();
    }

    /**
     * 读取数据
     *
     * @param din 输入流
     * @throws java.io.IOException 异常
     */
    public void read(DexInputStream din) throws IOException {
        int staticFieldCount = din.readUnsignedLeb128();
        int instanceFieldCount = din.readUnsignedLeb128();
        int directMethodCount = din.readUnsignedLeb128();
        int virtualMethodCount = din.readUnsignedLeb128();

        if (staticFieldCount > 0) {
            EncodeField fld;
            int lastIndex = 0;
            for (int i = 0; i < staticFieldCount; i++) {
                fld = new EncodeField(dex);
                fld.readEncode(din);
                staticFields.add(fld);
                lastIndex += fld.diff;
                fld.fieldIdIndex = lastIndex;
            }
        }
        if (instanceFieldCount > 0) {
            EncodeField fld;
            int lastIndex = 0;
            for (int i = 0; i < instanceFieldCount; i++) {
                fld = new EncodeField(dex);
                fld.readEncode(din);
                instanceFields.add(fld);
                lastIndex += fld.diff;
                fld.fieldIdIndex = lastIndex;
            }
        }
        if (directMethodCount > 0) {
            EncodeMethod mth;
            int lastIndex = 0;
            for (int i = 0; i < directMethodCount; i++) {
                mth = new EncodeMethod(dex);
                mth.readEncode(din);
                directMethods.add(mth);
                lastIndex += mth.diff;
                mth.methodIdIndex = lastIndex;
            }
        }
        if (virtualMethodCount > 0) {
            EncodeMethod mth;
            int lastIndex = 0;
            for (int i = 0; i < virtualMethodCount; i++) {
                mth = new EncodeMethod(dex);
                mth.readEncode(din);
                virtualMethods.add(mth);
                lastIndex += mth.diff;
                mth.methodIdIndex = lastIndex;
            }
        }
    }

    /**
     * 输出数据
     *
     * @param dout 输出流
     * @throws java.io.IOException 异常
     */
    public void write(DataOutputStream dout) throws IOException {
    }

    public void reloc(ByteArrayInputStream bin, int offset) throws IOException {
        int size = staticFields.size();
        EncodeMember member;
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                member = staticFields.get(i);
                member.reloc(bin, offset);
            }
        }
        size = instanceFields.size();
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                member = instanceFields.get(i);
                member.reloc(bin, offset);
            }
        }
        size = directMethods.size();
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                member = directMethods.get(i);
                member.reloc(bin, offset);
            }
        }
        size = virtualMethods.size();
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                member = virtualMethods.get(i);
                member.reloc(bin, offset);
            }
        }
    }

    public void reverseClass(JavaClass clazz) {
        int size = staticFields.size();
        ArrayList<EncodeField> flds;
        EncodeField fld;
        ConstantPool pool = clazz.getCP();
        if (size > 0) {
            flds = staticFields;
            FieldIdItem item;
            Field f;
            ConstantPool cp = pool;
            int cvname = -1;
            CPUtf8 cvn = new CPUtf8("ConstantValue");
            cvname = cp.indexOf(cvn);
            if (cvname == -1) {
                cvname = cp.addCPEntry(cvn);
            }
            for (int i = 0; i < size; i++) {
                fld = flds.get(i);
                item = fld.field;
                String fdName = item.nameItem.getDataItem().value;
                String fdType = item.typeItem.item.getDataItem().value;
                // 此方法会创建一个Field对象，并且如果需要，会向CP中添加 CPEntry
                f = Field.createField(cp, fdName, fdType);
                f.setAccess(fld.accessFlags);
                CstConstant cst = fld.constant;
                if (cst != null) {
                    ConstantValue cv = null;
                    if (cst instanceof CstBoolean) {
                        CstBoolean cb = (CstBoolean) cst;
                        int iv = (cb.getValue() ? 1 : 0);
                        CPInteger cint = new CPInteger(iv);
                        int vindex = cp.indexOf(cint);
                        if (vindex == -1) {
                            vindex = cp.addCPEntry(cint);
                        }
                        cv = new ConstantValue(cvname, 2, vindex);
                    } else if (cst instanceof CstDouble) {
                        CstDouble cd = (CstDouble) cst;
                        Object data = cd.getJavaData();
                        Double d = (Double) data;
                        CPDouble dd = new CPDouble(d.doubleValue());
                        int vindex = cp.indexOf(dd);
                        if (vindex == -1) {
                            vindex = cp.addCPEntry(dd);
                        }
                        cv = new ConstantValue(cvname, 2, vindex);
                    } else if (cst instanceof CstFloat) {
                        CstFloat cd = (CstFloat) cst;
                        Object data = cd.getJavaData();
                        Float d = (Float) data;
                        CPFloat dd = new CPFloat(d.floatValue());
                        int vindex = cp.indexOf(dd);
                        if (vindex == -1) {
                            vindex = cp.addCPEntry(dd);
                        }
                        cv = new ConstantValue(cvname, 2, vindex);
                    } else if (cst instanceof CstLiteralBits) {
                        CstLiteralBits lb = (CstLiteralBits) cst;
                        if (lb.isLongValue()) {
                            long lv = lb.getLiteralBits();
                            CPLong lg = new CPLong(lv);
                            int vindex = cp.indexOf(lg);
                            if (vindex == -1) {
                                vindex = cp.addCPEntry(lg);
                            }
                            cv = new ConstantValue(cvname, 2, vindex);
                        } else {
                            int iv = lb.getIntBits();
                            CPInteger ci = new CPInteger(iv);
                            int vindex = cp.indexOf(ci);
                            if (vindex == -1) {
                                vindex = cp.addCPEntry(ci);
                            }
                            cv = new ConstantValue(cvname, 2, vindex);
                        }
                    } else if (cst instanceof CstString) {
                        CstString str = (CstString) cst;
                        System.out.println("[Debug] str = " + str);
                    }
                    if(cv != null){
                        f.setConstantValue(cv);
                    }
                }
//                f.setConstantValue(new ConstantValue());
                clazz.addField(f);
            }
        }
        size = instanceFields.size();
        if (size > 0) {
            flds = instanceFields;
            FieldIdItem item;
            Field f;
            ConstantPool cp = pool;
            for (int i = 0; i < size; i++) {
                fld = flds.get(i);
                item = fld.field;
                String fdName = item.nameItem.getDataItem().value;
                String fdType = item.typeItem.item.getDataItem().value;
                // 此方法会创建一个Field对象，并且如果需要，会向CP中添加 CPEntry
                f = Field.createField(cp, fdName, fdType);
                f.setAccess(fld.accessFlags);
                clazz.addField(f);
            }
        }
        size = directMethods.size();
        ArrayList<EncodeMethod> mds;
        EncodeMethod md;
        if (size > 0) {
            mds = directMethods;
            MethodIdItem item;
            Method method;
            ConstantPool cp = pool;
            for (int i = 0; i < size; i++) {
                md = mds.get(i);
                item = md.method;
                String mdName = item.nameItem.getDataItem().value;
                String mdType = item.typeItem.getJavaDescript();
                // 此方法会创建一个Field对象，并且如果需要，会向CP中添加 CPEntry
                method = Method.createMethod(cp, mdName, mdType);
                method.setAccess(md.accessFlags);
                clazz.addMethod(method);
                if (md.code != null) {
                    Code cd = new Code(cp);
                    BCD bcd = cd.getBCD();
                    CodeItem citem = md.code;
                    int sz = citem.insnsSz;
                    // TODO Finish it.
                    boolean debug = false;
                    if (!debug) {
                        if (sz > 0) {
                            DalvInsnList insnList = citem.insns;
                            DalvInsn insn;
                            try {
                                for (int j = 0; j < sz; j++) {
                                    insn = insnList.indexOf(j);
                                    InsnConvert.convert(insn,bcd,cp);
                                }
                            } catch (IOException ioe) {

                            }
                        }
                    }
                }
            }
        }
        size = virtualMethods.size();
        if (size > 0) {
            mds = virtualMethods;
            MethodIdItem item;
            Method method;
            ConstantPool cp = pool;
            for (int i = 0; i < size; i++) {
                md = mds.get(i);
                item = md.method;
                String mdName = item.nameItem.getDataItem().value;
                String mdType = item.typeItem.getJavaDescript();
                // 此方法会创建一个Field对象，并且如果需要，会向CP中添加 CPEntry
                method = Method.createMethod(cp, mdName, mdType);
                method.setAccess(md.accessFlags);
                clazz.addMethod(method);
                if (md.code != null) {
                    Code cd = new Code(cp);
                    BCD bcd = cd.getBCD();
                    CodeItem citem = md.code;
                    // TODO 转换DalvCode 到 Java ByteCode
                }
            }
        }


    }
}
